#[macro_use]
extern crate log;

use std::thread;
use std::time::Duration;

use anyhow::{bail, Result};
use smol::net::{TcpListener, TcpStream};
use smol::prelude::{AsyncReadExt, AsyncWriteExt, FutureExt};
use smol::stream::StreamExt;
use smol::Timer;

fn main() {
    std::env::set_var("RUST_LOG", "trace");
    pretty_env_logger::init();

    info!("{:?} => program start", thread::current().id());
    match smol::block_on(run()) {
        Ok(_) => info!("Success"),
        Err(e) => error!("{}", e),
    }
}

async fn run() -> Result<()> {
    let listener = TcpListener::bind("0.0.0.0:9100").await?;

    info!("{:?} => listen on 0.0.0.0:9100", thread::current().id());
    while let Some(stream) = listener.incoming().next().await {
        let stream = stream?;
        smol::spawn(handle(stream)).detach();
    }

    Ok(())
}

async fn handle(mut stream: TcpStream) -> Result<()> {
    let timeout = |t| async move {
        Timer::after(Duration::from_secs(t)).await;
        Ok(())
    };

    loop {
        //读10位长度
        let mut len = [0_u8; 10];
        stream.read_exact(&mut len).or(timeout(30)).await?;
        let len = String::from_utf8_lossy(&len);
        let length: usize = len.parse()?;
        if length < 1 {
            warn!(
                "{:?} => receive length less then 1: {}",
                thread::current().id(),
                length
            );
            bail!("length is 0")
        }

        //读指定长度的报文
        let mut msg = vec![0_u8; length];
        stream.read_exact(&mut msg).or(timeout(30)).await?;
        let req = String::from_utf8(msg)?;
        info!(
            "{:?} => receive req: {}{}",
            thread::current().id(),
            len,
            req
        );

        let stream = stream.clone();
        debug!("{:?} => rpc start", thread::current().id());
        smol::spawn(rpc_deal(stream, req)).detach();
        debug!("{:?} => rpc done", thread::current().id());
    }
}

async fn rpc_deal(stream: TcpStream, req: impl ToString) {
    //按请求的时间戳%5
    // let mock_deal_time = chrono::Local::now().timestamp() % 5;
    Timer::after(Duration::from_secs(6)).await;

    let mut res = smol::fs::read_to_string("res.xml")
        .await
        .unwrap_or_else(|_| req.to_string());

    // res.push_str(" result");
    res = res.replace("\n", "");
    let res = format!("{:010}{}", res.len(), res);

    if let Err(e) = write_all(stream, &*res).await {
        error!("{:?} => send res fail: {}", thread::current().id(), e);
    } else {
        info!("{:?} => send res: {}", thread::current().id(), res);
    }
}

async fn write_all(mut stream: TcpStream, res: &str) -> Result<()> {
    stream.write_all(res.as_bytes()).await?;
    stream.flush().await?;
    // stream.close().await?;
    Ok(())
}
